"""
.. module:: resource_test

*Description*:

This module is the unit test for the Resource module.

*Constants*:

.. seealso:: class :class:`datastore.resource.Resource`
"""

# Imports
#
import logging
import unittest
import sys
import types

from datastore.resource import *
from datastore.uri import Uri
from datastore.spaces import Spaces

logger = logging.getLogger('yarra')
hdlr = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)

RESOURCE_HEADER = """<?xml version='1.0' encoding='UTF-8'?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" """ + \
"""xsi:schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd">"""

EMPTY_RESOURCE = RESOURCE_HEADER + \
"""</resource>"""

TEST_RESOURCE = """<?xml version='1.0' encoding='UTF-8'?>
<resource
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd">

    <metadata>
        <item key="term:base/datastore/resource/type" type="term:type/uri">package:type/manifest</item>
        <item key="dc:title" type="term:type/string">My first package</item>
        <item key="dc:description" type="term:type/string">My package has lots of useful functionality.</item>
    </metadata>

    <content>
        <list key="tasks">
            <item type="term:type/string">task one</item>
            <item type="term:type/string">task two</item>
        </list>
        <list key="files">
            <item type="term:type/string">file one</item>
            <item type="term:type/string">file two</item>
        </list>
        <item key="test_cdata" type="term:type/string"><![CDATA[<a>]]> before <![CDATA[a non-XML tag <hr>]]> after</item>
    </content>
</resource>
"""

RESOURCE_EVERY_TYPE = """<?xml version='1.0' encoding='UTF-8'?>
<resource id="001"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd">
    <metadata>
        <item key="dc:title" type="term:type/string">My first package</item>
        <item key="term:base/datastore/resource/type" type="term:type/uri">term:base/datastore/resource/test</item>
    </metadata>

    <content>
        <dict key="dict1" />
        <dict key="dict2">
            <item key="z" type="term:type/string">z value</item>
        </dict>
        <list key="list1" />
        <list key="list2">
            <item type="term:type/int">1</item>
            <item type="term:type/int">2</item>
            <item type="term:type/string">three</item>
        </list>
        <item key="type_boolean_t" type="term:type/boolean">True</item>
        <item key="type_boolean_f" type="term:type/boolean">False</item>
        <item key="type_float" type="term:type/float">3.1415926</item>
        <item key="type_int" type="term:type/int">123</item>
        <item key="term:version" type="term:type/int">456</item>
        <item key="type_long" type="term:type/long">100200300400500600</item>
        <item key="type_none" type="term:type/none" />
        <item key="type_string" type="term:type/string">a string.</item>
        <item key="empty_string" type="term:type/string"></item>
        <item key="type_uri" type="term:type/uri">term:version</item>
        <item key="type_base64" type="term:type/base64">eJwrzs9NVUhKLE41M1EoSa0o0QMANy0F7Q==</item>
        <tuple key="type_tuple1" />
        <tuple key="type_tuple2">
            <item type="term:type/int">1</item>
            <item type="term:type/int">2</item>
            <item type="term:type/string">three</item>
        </tuple>
        <item key="type_unicode" type="term:type/unicode">a unicode string.</item>
    </content>
</resource>
"""

class ResourceTest(unittest.TestCase):
                
    # ---------- ID tests ----------

    def test_id_case_01(self):
        """
        [resource] id - set_id
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        self.assertEqual(res1.get_id(), '001')

    def test_id_case_03(self):
        """
        [resource] id - get_single_metadata exception
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        try:
            res1.set_id(None)
            assert False
        except TypeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Resource ID cannot be None.")

    # ---------- Metadata tests ----------

    def test_metadata_case_01(self):
        """
        [resource] metadata - get_single_metadata
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('z'), "meta z")
        self.assertEqual(res1.get_metadata(Uri('y')), "meta y")
        self.assertEqual(res1.get_metadata(Uri('z')), "meta z")

    def test_metadata_case_02(self):
        """
        [resource] metadata - set multiple metadata with the same key
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.set_metadata(Uri('y'), "meta y", multiple=True)
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('y'), "meta y2")
        res1.set_metadata(Uri('z'), "meta z")
        self.assertEqual(len(res1.get_metadata(Uri('y'))), 3)
        self.assertEqual(res1.get_metadata(Uri('y')), 
                         ["meta y", "meta y", "meta y2"])
        self.assertEqual(res1.get_metadata(Uri('z')), 
                         "meta z")

    def test_metadata_case_03(self):
        """
        [resource] metadata - get_metadata single value
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('y'), "meta y")
        res1.set_metadata(Uri('y'), "last meta y")
        self.assertEqual(res1.get_metadata(Uri('y')), "last meta y")

    def test_metadata_case_04(self):
        """
        [resource] metadata - get_metadata key not found exception
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        try:
            dummy = res1.get_metadata(Uri('y'))
            assert False
        except IndexError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Metadata key (y) not found.")

    def test_metadata_case_05 (self):
        """
        [resource] metadata
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_metadata(Uri('m'), "meta m")
        res1.set_metadata(Uri('n'), "meta n")
        res1.set_id('001')
        self.assertEqual(res1.get_metadata(Uri('m')), "meta m")
        self.assertEqual(res1.get_metadata(Uri('n')), "meta n")
        self.assertEqual(res1.get_metadata_keys(),
            [Uri('term:base/datastore/resource/type'), Uri('m'), Uri('n')])
        res2 = Resource(res1.serialise())
        self.assertEqual(res2.get_metadata(Uri('m')), "meta m")
        self.assertEqual(res2.get_metadata(Uri('n')), "meta n")
        self.assertEqual(res2.metadata_has_key(Uri('m')), True)
        self.assertEqual(res2.metadata_has_key(Uri('dummy')), False)
        self.assertEqual(res2.get_metadata_keys(),
            [Uri('term:base/datastore/resource/type'), Uri('m'), Uri('n')])

    def test_metadata_case_06 (self):
        """
        [resource] metadata - delete metadata
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_metadata(Uri('m'), "meta m")
        res1.set_metadata(Uri('n'), "meta n", multiple=True)
        res1.set_metadata(Uri('n'), "meta n")
        res1.set_id('001')
        res2 = Resource(res1.serialise())
        
        self.assertEqual(res1.get_metadata(Uri('m')), "meta m")
        self.assertEqual(res1.get_metadata(Uri('n')), ["meta n", "meta n"])
        res1.del_metadata(Uri('m'), "meta m")
        self.assertEqual(res1.metadata_has_key(Uri('m')), False)
        res1.del_metadata(Uri('n'), "meta n")
        self.assertEqual(res1.metadata_has_key(Uri('n')), True)
        res1.del_metadata(Uri('n'), "meta n")
        self.assertEqual(res1.metadata_has_key(Uri('n')), False)
        self.assertEqual(res1.get_metadata_keys(), 
                         [Uri('term:base/datastore/resource/type')])
        
        # and make sure that all is OK in a copy of the resource
        self.assertEqual(res2.get_metadata(Uri('m')), "meta m")
        self.assertEqual(res2.get_metadata(Uri('n')), ["meta n", "meta n"])
        res2.del_metadata(Uri('m'), "meta m")
        self.assertEqual(res2.metadata_has_key(Uri('m')), False)
        res2.del_metadata(Uri('n'), "meta n")
        self.assertEqual(res2.metadata_has_key(Uri('n')), True)
        res2.del_metadata(Uri('n'), "meta n")
        self.assertEqual(res2.metadata_has_key(Uri('n')), False)
        self.assertEqual(res1.get_metadata_keys(), 
                         [Uri('term:base/datastore/resource/type')])

    def test_metadata_case_07 (self):
        """
        [resource] metadata - fetch non-existent metadata
        """
        res1 = Resource(type='term:base/datastore/resource/test')       
        try:
            res1.get_metadata(Uri('fred'))
            assert False
        except IndexError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Metadata key (fred) not found.")
        
    def test_metadata_case_08(self):
        """
        [resource] metadata with missing key
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <metadata>
                <item type="%s">z</item>
            </metadata></resource>
            """ % term.type.string
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing key for metadata element.")

    def test_metadata_case_09(self):
        """
        [resource] metadata type error on _get_metadata_key
        """
        res = Resource(type='term:base/datastore/resource/test')
        try:
            res.metadata_has_key('not a uri')
            assert False
        except TypeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Key (not a uri) must be a Uri.")

    def test_metadata_case_10(self):
        """
        [resource] metadata type error on get
        """
        res = Resource(type='term:base/datastore/resource/test')
        try:
            res.get_metadata('not a uri')
            assert False
        except TypeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Key (not a uri) must be a Uri.")

    def test_metadata_case_11(self):
        """
        [resource] metadata delete 2
        """
        uri = Uri('internal:test')
        res = Resource(type='term:base/datastore/resource/test')
        res.set_metadata(uri, 123)
        self.assertEqual(res.get_metadata(uri), 123)
        try:
            res.del_metadata(uri, 123)
            assert True
        except:
            assert False

    def test_metadata_case_12(self):
        """
        [resource] metadata delete 3
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.set_metadata(Uri('term:test'), 'test')
        try:
            res.del_metadata(Uri('term:test'))
            assert True
        except:
            assert False

    def test_metadata_case_12a(self):
        """
        [resource] metadata delete 4
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.set_metadata(Uri('term:test'), 'test a', multiple=True)
        res.set_metadata(Uri('term:test'), 'test b', multiple=True)
        res.set_metadata(Uri('term:test'), 'test c', multiple=True)
        try:
            res.del_metadata(Uri('term:test'))
            assert True
        except:
            assert False

    def test_metadata_case_13(self):
        """
        [resource] metadata type error on delete
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.set_metadata(Uri('term:test'), 999, multiple=True)
        try:
            res.del_metadata(Uri('term:test'), 123)
            assert False
        except IndexError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Metadata value (123) not found for key (term:test).")

    def test_metadata_case_14(self):
        """
        [resource] metadata single to multiple exception
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.set_metadata(Uri('term:test'), 999)
        try:
            res.set_metadata(Uri('term:test'), 123, multiple=True)
            assert False
        except TypeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "Cannot convert a single entry (term:test) to multiple.")

    def test_dc_title(self):
        """
        [resource] metadata dc_title convenience method
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.set_metadata(Uri('dc:title'), 'Hi')
        self.assertTrue(res.dc_title(), 'Hi')
        res.dc_title("""
                Hi
                """)
        self.assertTrue(res.dc_title(), 'Hi')
        self.assertTrue(res.dc_title(), res.get_metadata(Uri('dc:title')))

    def test_dc_description(self):
        """
        [resource] metadata dc_description convenience method
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.set_metadata(Uri('dc:description'), 'Hi')
        self.assertTrue(res.dc_description(), 'Hi')
        res.dc_description("""
                Hi
                """)
        self.assertTrue(res.dc_description(), 'Hi')
        self.assertTrue(res.dc_description(), 
                        res.get_metadata(Uri('dc:description')))

    # ---------- Serialise tests ----------
    
    def test_serialise_case_01(self):
        """
        [resource] serialise a valid but empty resource
        """
        res_text = RESOURCE_HEADER + """<content /></resource>"""
        self.assertEqual(res_text, Resource(empty=True),
            "Resources not equal: \n%s\n%s\n" % (res_text, 
                                                 Resource(empty=True)))

    def test_serialise_case_02(self):
        """
        [resource] serialise each supported type
        """
        res = Resource(type='term:base/datastore/resource/test')
        dc = Uri('dc:')
        res.set_metadata(dc.title, 'My first package')
        res.set_id('001')
        res.type_none = None
        res.type_boolean_t = True
        res.type_boolean_f = False
        res.set_var(Uri('term:version'), 456)
        res.type_int = 123
        res.type_long = 100200300400500600
        res.type_float = 3.1415926
        res.type_unicode = u'a unicode string.'
        res.type_tuple1 = ()
        res.type_tuple2 = (1, 2, 'three')
        res.type_string = 'a string.'
        res.empty_string = ""
        res.type_uri = Uri('term:version')
        res.type_base64 = Data("some base64 text.")
        res.list1 = []
        res.list2 = [1, 2, 'three']
        res.dict1 = dict()
        res.dict2 = {'z': "z value"}
        # this resource contains a resource reference
        res_et = Resource(RESOURCE_EVERY_TYPE)
        self.assertEqual(res.serialise(), res_et,
            "Resources not equal: \n%s\n%s\n" % (res.serialise(), res_et))

    def test_serialise_case_03(self):
        """
        [resource] serialise and deserialise the same resource
        """
        self.assertEqual(Resource(TEST_RESOURCE), Resource(
            str(Resource(str(Resource(str(Resource(TEST_RESOURCE))))))),
            "Resources not equal: \n%s\n%s\n" % 
                (Resource(TEST_RESOURCE), Resource(TEST_RESOURCE)))

    def test_serialise_case_04(self):
        """
        [resource] serialise - ignore class private variables
        """
        class Z(Resource):
            pass
        res1 = Z(type='term:base/datastore/resource/test')
        res1.__priv = "a private variable"
        res2 = Resource(type='term:base/datastore/resource/test')
        self.assertEqual(res1, res2,
            "Resources not equal: \n%s\n%s\n" % (res1, res2))

    def test_serialise_case_05(self):
        """
        [resource] serialise - serialise_content()
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.a = "a var"
        res1.b = 123
        res1.set_metadata(Uri('m'), "meta")
        res1.set_id('001')
        self.assertEqual(res1.a, "a var")
        self.assertEqual(res1.b, 123)
        self.assertEqual(res1.get_id(), '001')
        self.assertEqual(res1.get_metadata(Uri('m')), "meta")
        res2 = Resource(res1.serialise_content())
        self.assertEqual(res2.a, "a var")
        self.assertEqual(res2.b, 123)
        self.assertEqual(res2.get_id(), None)
        self.assertEqual(res2.metadata_has_key(Uri('m')), False)

    def test_serialise_case_06(self):
        """
        [resource] serialise an unknown type
        """
        class Z(object):
            pass
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.z = Z()
        logger.log(logging.INFO, "Expecting 'WARNING " + \
            """Unknown type for item 'z'. Setting to string.'""")
        res2 = Resource(res1.serialise())
        self.assertEqual(type(res2.z), types.StringType,
            "Resource item type is not string: \n%s" % res2)

    def test_serialise_case_07(self):
        """
        [resource] serialise an unknown type with strict checking
        """
        class Z(object):
            pass
        res1 = Resource(strict=True, type='term:base/datastore/resource/test')
        res1.z = Z()
        try:
            res2 = Resource(res1.serialise())
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unknown type for item: z.")

    def test_serialise_case_08(self):
        """
        [resource] serialise - deserialise - verify reserved XML characters are 
        handled correctly
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.content = "<html>some html & more html</html>"
        #res1.b = 123
        res1.set_metadata(Uri('m'), "meta")
        res1.set_id('001')
        res2 = Resource(res1.serialise())
        self.assertEqual(res1.content, "<html>some html & more html</html>")

    # ---------- Deserialise positive tests ----------
    
    def test_deserialise_case_01(self):
        """
        [resource] deserialise and check values
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.deserialise(RESOURCE_EVERY_TYPE)
        assert(res.type_none == None)
        assert(res.type_boolean_t == True)
        assert(res.type_boolean_f == False)
        assert(res.type_int == 123)
        assert(res.type_long == 100200300400500600)
        assert(res.type_float == 3.1415926)
        assert(res.type_unicode == u'a unicode string.')
        assert(res.type_tuple1 == ())
        assert(res.type_tuple2 == (1, 2, 'three'))
        assert(res.type_string == 'a string.')
        assert(res.empty_string == '')
        assert(res.list1 == [])
        assert(res.list2 == [1, 2, 'three'])
        assert(res.dict1 == dict())
        assert(res.dict2 == {'z': "z value"})
        # test retrieving metadata by full and shorten uri
        f = 'http://node.internal/data_dictionary/base/datastore/resource/type'
        self.assertEqual(res.get_metadata(
            Uri(f)), 
            Uri('term:base/datastore/resource/test'))
        self.assertEqual(res.get_metadata(
            Uri('term:base/datastore/resource/type')), 
            Uri('term:base/datastore/resource/test'))
        # the key is always stored as a string
        assert(res.get_var('term:version') == 456)
    
    def test_deserialise_case_02(self):
        """
        [resource] deserialise CDATA
        """
        res = Resource(type='term:base/datastore/resource/test')
        term = Uri('term:')
        res.deserialise(TEST_RESOURCE)
        test = "<a> before a non-XML tag <hr> after"
        assert(res.test_cdata == test)

    # ---------- Deserialise negative tests ----------
    
    def test_deserialise_negative_case_01(self):
        """
        [resource] deserialise from an invalid type
        """
        td = "<zzz />"
        try:
            Resource(999)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Expected string or buffer.")

    def test_deserialise_negative_case_02(self):
        """
        [resource] deserialise - wrong root node (not resource)
        """
        td = "<zzz />"
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing 'resource' root element.")

    def test_deserialise_negative_case_03(self):
        """
        [resource] deserialise with missing item key
        """
        td = RESOURCE_HEADER + """
            <content>
                <item />
            </content></resource>
            """
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing key for element: item.")

    def test_deserialise_negative_case_04(self):
        """
        [resource] deserialise with missing item type
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="%s/z" />
            </content></resource>
            """ % term.type
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Missing type for element: item.")

    def test_deserialise_negative_case_05(self):
        """
        [resource] deserialise with incorrect boolean value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % term.type.boolean
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid boolean value for element(item).")
 
    def test_deserialise_negative_case_06(self):
        """
        [resource] deserialise with incorrect int value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % term.type.int
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid int value for element(item).")

    def test_deserialise_negative_case_07(self):
        """
        [resource] deserialise with incorrect long value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % term.type.long
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid long value for element(item).")

    def test_deserialise_negative_case_08(self):
        """
        [resource] deserialise with incorrect float value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z</item>
            </content></resource>
            """ % term.type.float
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid float value for element(item).")

    def test_deserialise_negative_case_09(self):
        """
        [resource] deserialise with incorrect unicode value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">\x80abc</item>
            </content></resource>
            """ % term.type.unicode
        try:
            # The error is actually detected during the XML parse.
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Expat parse error: not well-formed (invalid token): line 4, column 55")

    def test_deserialise_negative_case_11(self):
        """
        [resource] deserialise with an unknown element
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <z key="z" type="%s" />
            </content></resource>
            """ % term.type.none
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unknown element(z).")

    def test_deserialise_negative_case_12(self):
        """
        [resource] deserialise with an int converted to a long
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">100200300400500600700800900</item>
            </content></resource>
            """ % term.type.int
        res = Resource(td)
        # z is autmatically converted to a long by Python
        self.assertEqual(type(res.z), types.LongType)

    def test_deserialise_negative_case_13(self):
        """
        [resource] deserialise with an invalid int
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s">z100200300400500600700800900</item>
            </content></resource>
            """ % term.type.int
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Invalid int value for element(item).")

    def test_deserialise_negative_case_14(self):
        """
        [resource] serialise with an unknown type
        """
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="term:type/fred">abc</item>
            </content></resource>
            """
        Resource(td)
        assert True

    # cases for Boolean, Int, Long, Float, Unicode, Tuple, and String with no value.
 
    def test_deserialise_negative_case_none_boolean(self):
        """
        [resource] deserialise with a boolean with no value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % term.type.boolean
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % term.type.boolean)

    def test_deserialise_negative_case_none_int(self):
        """
        [resource] deserialise with a int with no value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % term.type.int
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % term.type.int)
 
    def test_deserialise_negative_case_none_long(self):
        """
        [resource] deserialise with a long with no value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % term.type.long
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % term.type.long)
 
    def test_deserialise_negative_case_none_float(self):
        """
        [resource] deserialise with a float with no value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % term.type.float
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % term.type.float)
 
    def test_deserialise_negative_case_none_unicode(self):
        """
        [resource] deserialise with a unicode with no value
        """
        term = Uri('term:')
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="%s" />
            </content></resource>
            """ % term.type.unicode
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (%s)." % term.type.unicode)

    def test_deserialise_negative_case_none_tuple(self):
        """
        [resource] deserialise with a tuple with no value
        """
        td = RESOURCE_HEADER + """
            <content>
                <item key="z" type="tuple" />
            </content></resource>
            """
        try:
            res = Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Element text is None for: z (tuple).")

    def test_deserialise_negative_incorrect_element(self):
        """
        [resource] deserialise with incorrect element under resource
        """
        td = RESOURCE_HEADER + """
                <zzz />
            </resource>
            """
        try:
            Resource(td)
            assert False
        except DeserialisationException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unknown element under resource: zzz.")

    # ---------- Comparison tests ----------

    def test_compare_case_01(self):
        """
        [resource] compare - equivalent resources
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource(type='term:base/datastore/resource/test')
        res2.type_int = 123
        res2.type_string = 'a string.'
        self.assertEqual(res1, res2,
            "Resources not equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_02(self):
        """
        [resource] compare - equivalent resources with the same id
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource(type='term:base/datastore/resource/test')
        res2.set_id('001')
        res2.type_int = 123
        res2.type_string = 'a string.'
        self.assertEqual(res1, res2,
            "Resources not equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_03(self):
        """
        [resource] compare - equivalent resources with mismatched id's
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource(type='term:base/datastore/resource/test')
        res2.type_int = 123
        res2.type_string = 'a string.'
        self.assertNotEqual(res1, res2,
            "Resources incorrectly equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_04(self):
        """
        [resource] compare - resource sort order
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.type_int = 123
        res1.type_string = 'a string.'
        res2 = Resource(type='term:base/datastore/resource/test')
        res2.set_id('002')
        res2.type_int = 123
        res2.type_string = 'a string.'
        if res1 < res2:
            assert True
        else:
            self.fail("comparison test failed.")

    def test_compare_case_05(self):
        """
        [resource] compare - different resources
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.type_string = 'a string.'
        res2 = Resource(type='term:base/datastore/resource/test')
        res2.set_id('002')
        res2.type_int = 123
        self.assertNotEqual(res1, res2,
            "Resources incorrectly equal: \n%s\n%s\n" % (res1, res2))

    def test_compare_case_06(self):
        """
        [resource] compare - resource and something else
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.type_string = 'a string.'
        self.assertNotEqual(res1, dict(),
            "Resource incorrectly matches with dict(): \n%s" % res1)

    def test_compare_case_07(self):
        """
        [resource] compare - other is none
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_id('001')
        res1.type_string = 'a string.'
        self.assertNotEqual(res1, None,
            "Resource incorrectly matches with None: \n%s" % res1)


    # ---------- Other tests ----------

    def test_other_comment_case_01(self):
        """
        [resource] other - test text mark-up and comments
        """
        td = RESOURCE_HEADER + """
            <!-- comment -->
            <metadata>
                <item key="term:base/datastore/resource/type" 
                    type="term:type/uri">package:type/manifest</item>
                <!-- comment -->
                <item key="dc:description" type="term:type/string">
                My package has lots of useful functionality.</item>
                <!-- comment -->
                <item key="dc:title" type="term:type/string">
                My first package</item>
                <!-- comment -->
                <item key="term:version" type="term:type/string">0.1</item>
                <!-- comment -->
            </metadata>
            <!-- comment -->
            <content>
                <!-- comment -->
                <list key="aaa">
                    <!-- comment -->
                    <item type="term:type/string">module <br /> one</item>
                    <!-- comment -->
                    <item type="term:type/string">
                    module <!-- comment --> two</item>
                </list>
                <!-- comment -->
                <list key="bbb">
                    <item type="term:type/string">task one</item>
                    <!-- comment -->
                    <item type="term:type/string">task two</item>
                </list>
                <!-- comment -->
                <tuple key="ccc">
                    <item type="term:type/string">task one</item>
                    <!-- comment -->
                    <item type="term:type/string">task two</item>
                </tuple>
                <!-- comment -->
            </content><!-- comment --></resource>
            """
        res = Resource(td)
        test = "module  one"
        self.assertEqual(res.aaa[0], test,
            "Strings should be equal: \n%s\n%s\n" % (res.aaa[0], test))

    def test_other_get_metadata_keys(self):
        """
        [resource] other get_metadata_keys
        """
        res = Resource(TEST_RESOURCE)
        keys = res.get_metadata_keys()
        expected_keys = [Uri('term:base/datastore/resource/type'),
                         Uri('dc:description'),
                         Uri('dc:title')
                         ]
        for expected_key in expected_keys:
            self.assertTrue(expected_key in keys, 
                "Resources not equal: expected_key %s not found" % \
                (expected_key))

    def test_other_get_resource_type(self):
        """
        [resource] other get_resource_type
        """
        res = Resource(TEST_RESOURCE)
        package = Uri('package:')
        type = res.get_type()
        self.assertEqual(type, package.type.manifest,
            "Resources not equal: \n%s\n%s\n" % 
                (type, package.type.manifest))
        res.set_type(Uri('abc'))
        self.assertEqual(res.get_type(), Uri('abc'),
            "Resources not equal: \n%s\n%s\n" % 
                (res.get_type(), 'abc'))

#    def test_other_load_from_file(self):
#        """[resource] other load_from_file"""
#        res = Resource(type='term:base/datastore/resource/test')
#        #import os
#        #print('Directory:', os.listdir("."))
#        #res.load_from_file('./configuration.xml')
#        try:
#            res.load_from_file('./configuration.xml')
#        except:
#            res.load_from_file('datastore/configuration.xml')
#        res_type = res.get_type()
#        self.assertEqual(res_type, Uri('term:base/runtime/configuration'),
#            "Resource types not equal: \n%s\n%s\n" % 
#                (res_type, Uri('term:base/runtime/configuration')))
#        cont = res.definitions['dc:contributor']['dc:title']
#        self.assertEqual(cont, 'DC - Contributor',
#            "Resource contributers not equal: \n%s\n%s\n" % 
#                (cont, 'DC - Contributor'))

    def test_pretty_print(self):
        """
        [resource] other pretty_print
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.set_id('001')
        res.set_metadata(Uri("dc:title"), "value")
        #print(res.serialise())
        self.assertEqual(res.pretty_print(), 
"""<resource {http://www.w3.org/2001/XMLSchema-instance}schemaLocation="http://yarra.googlecode.com/files/resource-1.0.xsd" id="001">
  <metadata>
    <item type="term:type/uri" key="term:base/datastore/resource/type">
      term:base/datastore/resource/test
    </item>
    <item type="term:type/string" key="dc:title">
      value
    </item>
  </metadata>
  <content>
  </content>
</resource>
""")
        
    def test_var_methods(self):
        """
        [resource] other get_vars, get_var, set_var, del_var
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.a = 10
        res.b = 'aaa'
        res.set_var('1#2', 123)
        self.assertEqual(res.a, 10)
        self.assertEqual(res.get_var('a'), 10)
        self.assertEqual(res.b, 'aaa')
        self.assertEqual(res.get_var('1#2'), 123)
        self.assertTrue(res.has_var('1#2'))
        self.assertTrue(res.get_var('zzz', 69), 69)
        self.assertTrue(res.get_var('a', 69), 10)
        res.del_var('a')
        try:
            print(res.a)
            assert False
        except AttributeError:
            self.assertEqual(str(sys.exc_info()[1]),
                "'Resource' object has no attribute 'a'")

    def test_get_var_part(self):
        """
        [resource] other get_var_part
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.a = "qaaa"
        res.b = [['a1', ['qaa1', 'qaa2']], ['b1', ['qbb1', 'qbb2']], ['c1', ['qcc1', 'qcc2']]]
        res.c = "qccc"
        res.d = "qddd"
        res.x = [{'a1':'q11','a2':'q12'}, {'a1':'q21','a2':'q22'}, {'a1':'q31','a2':'q32'}]
        y = {0:'q50',1:'q51'}
        res.y = y
        z = {'0':'q60','1':'q61'}
        res.z = z
        self.assertEqual(res.get_var_part(None), res.get_vars())
#        self.assertEqual(res.get_var_part(''), res.get_vars())
        self.assertEqual(res.get_var_part('a'), "qaaa")
        self.assertEqual(res.get_var_part('b.1.1.1'), "qbb2")
        self.assertEqual(res.get_var_part('x.1.a2'), "q22")
        self.assertEqual(res.get_var_part('y'), y)
        self.assertEqual(res.get_var_part('y.1'), 'q51')
        self.assertEqual(res.get_var_part('z.1'), 'q61')
        # test getting an empty part and a part with a null fragment specified.
        self.assertEqual(res.get_var_part(''), res.get_vars())
        # and a part of None
        self.assertEqual(res.get_var_part(None), res.get_vars())
        try:
            res.get_var_part(0)
            assert False
        except GetPartException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Part '0' is not a string.")
        try:
            res.get_var_part(3.1415926)
            assert False
        except GetPartException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Part '3.1415926' is not a string.")
        try:
            res.get_var_part('b.a2')
            assert False
        except GetPartException:
            self.assertEqual(str(sys.exc_info()[1]),
                "Unable to get 'b.a2' for the Resource.")

    def test_repr_01(self):
        """
        [resource] repr basic tests
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.a = "qaaa"
        res.b = [['a1', ['qaa1', "qa'a2"]], ['b1', ['qbb1', Uri('qbb2')]], ['c1', ['qcc1', 'qcc2']]]
        res.c = "qccc"
        res.d1 = Data()
        res.d2 = Data(encoded='')
        res.d3 = Data("qd'dd")
        res.x = [{'a1':'q11','a2':'q12'}, {'a1':'q21','a2':'q22'}, {'a1':'q31','a2':'q32'}]
        y = {0:'q50',1:'q51'}
        res.y = y
        z = {'0':'q60','1':'q61'}
        res.z = z
        d = ResourceDict()
        d.a = 3
        d.b = 5
        d.u = Uri('urn:test')
        d[Uri('urn:aaa')] = 'aaa'
        d['z'] = 7
        res.rd = d
        #print(repr(res))
        self.assertEqual(res, Resource(repr=repr(res)))
        a = Data("hello")
        b = ['aaa', a]
        self.assertEqual(repr(b), "['aaa', Data(encoded='eJzLSM3JyQcABiwCFQ==')]")

    def test_repr_02(self):
        """
        [resource] repr invalid resource
        """
        res = Resource(type='term:base/datastore/resource/test')
        res.a = logger
        try:
            res = Resource(repr=repr(res))
            assert False
        except DeserialisationException:
            self.assertTrue(True)

    def test_dict_attribute_01(self):
        """
        [resource] attribute access to dict values
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.dict = ResourceDict()
        res1.dict.aaa = 123
        res1.dict.bbb = ResourceDict()
        res1.dict.bbb.ccc = 456
        res1.set_id('001')
        res1_xml = res1.serialise()
        res2 = Resource(res1_xml)
        self.assertEqual(res1.dict.aaa, 123)
        self.assertEqual(res1.dict.bbb.ccc, 456)
        self.assertEqual(res2.dict.aaa, 123)
        self.assertEqual(res2.dict.bbb.ccc, 456)
        self.assertEqual(res1.dict['aaa'], 123)
        self.assertEqual(res1.dict['bbb']['ccc'], 456)

    def test_type_01(self):
        """
        [resource] test setting type in the constructor
        """
        type_str = 'term:base/datastore/test_resource'
        type_uri = Uri(type_str)
        res1 = Resource(type=type_str)
        res2 = Resource(type=type_uri)
        res3 = Resource(type='term:base/datastore/resource/test')
        res3.set_type(type_uri)
        self.assertEqual(res1.get_type(), type_uri)
        self.assertEqual(res2.get_type(), type_uri)
        self.assertEqual(res3.get_type(), type_uri)

    def test_copy_01(self):
        """
        [resource] test copying a resource
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.aaa = 10
        res2 = res1
        res2.aaa = 20
        self.assertEqual(res1.aaa, res2.aaa)

    def test_copy_02(self):
        """
        [resource] test copying a resource (not equal)
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.aaa = 10
        res2 = res1.copy()
        res2.aaa = 20
        self.assertNotEqual(res1.aaa, res2.aaa)

    def test_base64_01(self):
        """
        [resource] test base64 encode and decode
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.aaa = Data("hello")
        res2 = res1.copy()
        self.assertEqual(res2.aaa, Data("hello"))

    def test_base64_02(self):
        """
        [resource] test base64 with no data
        """
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.aaa = Data(None)
        res2 = res1.copy()
        self.assertEqual(res2.aaa, Data(None))

    def test_base64_03(self):
        """
        [resource] test base64 with an empty string
        """
        
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.aaa = Data('')
        res2 = res1.copy()
        self.assertEqual(res2.aaa, Data(''))

    # ---------- Validation tests ----------

    def test_validation_01(self):
        """
        [resource] test resource validation
        """
        spaces = Spaces()
        # standard definitions
        res1 = Resource(type='term:base/runtime/definition')
        spaces.put(Uri('term:base/runtime/definition'), res1, warning=False)
        res1 = Resource(type='term:base/runtime/definition')
        spaces.put(Uri('term:base/datastore/resource/type'), res1,warning=False)
        res1 = Resource(type='term:base/runtime/definition')
        spaces.put(Uri('dc:title'), res1)
        res1 = Resource(type='term:base/runtime/definition')
        spaces.put(Uri('dc:description'), res1)
        # definition of our test resource
        res1 = Resource(type='term:base/runtime/definition')
        res1.metadata = {}
        res1.metadata['dc:title'] = 'defined dc:title'
        res1.content = {}
        res1.content['aaa'] = 'defined aaa'
        spaces.put(Uri('term:base/datastore/resource/test'), res1)
        # the test resource
        res1 = Resource(type='term:base/datastore/resource/test')
        res1.set_metadata(Uri('dc:title'), "title...")
        res1.aaa = 1.0
        #self.assertTrue(res1.validate(spaces))

if __name__ == "__main__":
    unittest.main()